package com.ouc.yyi.springcloud.gateway.filter.request;

import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
import cn.hutool.json.JSONUtil;
import com.ouc.yyi.springcloud.gateway.consts.Const;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.support.BodyInserterContext;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ReactiveHttpOutputMessage;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.HandlerStrategies;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.charset.Charset;

/**
 * @description: 创建全局解密拦截器，通过该拦截器对数据进行解密, 从modifyRequestBody中获取数据并修改数据
 * 从上下文exchange中获取到对应的body体信息进行机密
 * @author: yyi
 * @date: 2022/9/16 8:48
 * @version: 1.0
 **/
@Component
@Slf4j
public class DecryptionFilter implements GlobalFilter, Ordered {


    private static final SymmetricCrypto sm4 = SmUtil.sm4(Const.EN_KEY.getBytes());

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("进入请求拦截器，对请求参数进行解密");
        HttpMethod method = exchange.getRequest().getMethod();
        if (method != HttpMethod.POST) {
            return chain.filter(exchange);
        }
        return decryBody(exchange, chain);
    }

    private Mono<Void> decryBody(ServerWebExchange exchange, GatewayFilterChain chain) {
        // mediaType,该方法仅针对application/json的类型处理
        MediaType mediaType = exchange.getRequest().getHeaders().getContentType();
        // read & modify body
        ServerRequest serverRequest = ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders());
        Mono<String> modifiedBody = serverRequest.bodyToMono(String.class)
                .flatMap(body -> {
                    if (MediaType.APPLICATION_JSON.isCompatibleWith(mediaType)) {
                        log.info("*********request拦截器加密[前]的requestBody*********" + body);
                        String value = JSONUtil.parseObj(body).get("value").toString();
                        String s1 = sm4.decryptStr(value, Charset.defaultCharset());
                        log.info("*********request拦截器加密[后]的requestBody*********" + s1);
                        return Mono.just(s1);
                    }
                    return Mono.empty();
                });
        BodyInserter<Mono<String>, ReactiveHttpOutputMessage> bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
        HttpHeaders headers = new HttpHeaders();
        headers.putAll(exchange.getRequest().getHeaders());
        headers.remove(HttpHeaders.CONTENT_LENGTH);
        MyCachedBodyOutputMessage outputMessage = new MyCachedBodyOutputMessage(exchange, headers);
        return bodyInserter.insert(outputMessage, new BodyInserterContext())
                .then(Mono.defer(() -> {
                    ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator(
                            exchange.getRequest()) {
                        @Override
                        public HttpHeaders getHeaders() {
                            long contentLength = headers.getContentLength();
                            HttpHeaders httpHeaders = new HttpHeaders();
                            httpHeaders.putAll(super.getHeaders());
                            if (contentLength > 0) {
                                httpHeaders.setContentLength(contentLength);
                            } else {
                                httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
                            }
                            return httpHeaders;
                        }

                        @Override
                        public Flux<DataBuffer> getBody() {
                            return outputMessage.getBody();
                        }
                    };
                    return chain.filter(exchange.mutate().request(decorator).build());
                }));
    }

    @Override
    public int getOrder() {
        return 0;
    }

    public static void main(String[] args) {
        String value = "{\n" +
                "    \"mobile\": \"13780631855\"\n" +
                "}";
        String desc = sm4.encryptHex(value, Charset.defaultCharset());
        System.err.println(desc);
    }
}
